#include <stdio.h>          // 标准输入输出（printf、perror）
#include <fcntl.h>          // 文件控制（open、O_RDWR）
#include <sys/ioctl.h>      // IOCTL命令（_IOW、_IOR）
#include <sys/mman.h>       // 内存映射（可选，本程序未用）
#include <stdlib.h>         // 动态内存分配（malloc、free）
#include <string.h>         // 字符串操作（memcpy等，本程序未用）
#include <stdint.h>         // 标准整数类型（u32、uint32_t）
#include <unistd.h>         // 系统调用（read、close、sleep）
#include <sys/types.h>      // 数据类型（ssize_t、size_t）

// 与驱动一致的IOCTL命令定义（保持不变）
#define PCIE_CDEV_MAGIC 'P'
#define CMD_SET_ADDR_LOW     _IOW(PCIE_CDEV_MAGIC, 0, uint32_t)  // 替换u32为uint32_t（更标准）
#define CMD_SET_ADDR_HIGH    _IOW(PCIE_CDEV_MAGIC, 1, uint32_t)
#define CMD_SET_CPLD_ADDR    _IOW(PCIE_CDEV_MAGIC, 2, uint32_t)
#define CMD_SET_LEN          _IOW(PCIE_CDEV_MAGIC, 3, uint32_t)
#define CMD_START_DMA        _IO(PCIE_CDEV_MAGIC, 4)
#define CMD_GET_DMA_PHYS_LOW _IOR(PCIE_CDEV_MAGIC, 5, uint32_t)
#define CMD_GET_DMA_PHYS_HIGH _IOR(PCIE_CDEV_MAGIC, 6, uint32_t)
#define CMD_SET_DMA_SIZE     _IOW(PCIE_CDEV_MAGIC, 7, size_t)

// 设备节点路径（根据目标平台调整，如嵌入式系统可能不同）
#define DEV_PATH "/dev/pcie_cdev_dma_0002:21:00.0"  
// 图片尺寸（根据目标平台内存大小调整，避免内存溢出）
#define PCIE_DMA_SIZE 1 * 1024    // 256KB

int main(int argc, char *argv[]) {  // 添加argc/argv（可选，增强灵活性）
    int fd = open(DEV_PATH, O_RDWR);
    if (fd < 0) {
        perror("Failed to open device");
        return EXIT_FAILURE;  // 使用标准退出码（更规范）
    }

    // 1. 设置DMA内存大小（匹配图片尺寸）
    size_t dma_size = PCIE_DMA_SIZE;
    if (ioctl(fd, CMD_SET_DMA_SIZE, &dma_size) < 0) {
        perror("Failed to set DMA size");
        close(fd);
        return EXIT_FAILURE;
    }

    // 2. 获取DMA物理地址（拆分为低32位/高32位）
    uint32_t addr_low = 0, addr_high = 0;  // 使用uint32_t（stdint.h定义，跨平台兼容）
    if (ioctl(fd, CMD_GET_DMA_PHYS_LOW, &addr_low) < 0 ||
        ioctl(fd, CMD_GET_DMA_PHYS_HIGH, &addr_high) < 0) {
        perror("Failed to get DMA phys address");
        close(fd);
        return EXIT_FAILURE;
    }
    printf("DMA physical address: 0x%08x%08x\n", addr_high, addr_low);  // 64位地址格式（高32位+低32位）

    // 3. 设置寄存器参数（按流程图顺序）
    // uint32_t cpld_addr = 0x1000;    // CpID初始地址（需与硬件匹配）
    uint32_t cpld_addr = 0x00;    // CpID初始地址（需与硬件匹配）
    uint32_t packet_len = (PCIE_DMA_SIZE >> 2 ) - 1;  // 数据包长度（图片大小）
    if (ioctl(fd, CMD_SET_ADDR_LOW, &addr_low) < 0 ||
        ioctl(fd, CMD_SET_ADDR_HIGH, &addr_high) < 0 ||
        ioctl(fd, CMD_SET_CPLD_ADDR, &cpld_addr) < 0 ||
        ioctl(fd, CMD_SET_LEN, &packet_len) < 0) {
        perror("Failed to set registers");
        close(fd);
        return EXIT_FAILURE;
    }
    printf("DMA set address success.\n");

    // 4. 启动DMA（等待完成）
    if (ioctl(fd, CMD_START_DMA) < 0) {
        perror("Failed to start DMA");
        close(fd);
        return EXIT_FAILURE;
    }

    // 5. 读取图片数据（从DMA内存到用户空间）
    char *pcie_dma_buf = malloc(PCIE_DMA_SIZE);
    if (!pcie_dma_buf) {
        perror("Failed to allocate pcie_dma buffer");
        close(fd);
        return EXIT_FAILURE;
    }
    ssize_t read_len = read(fd, pcie_dma_buf, PCIE_DMA_SIZE);  // ssize_t（带符号的size_t，兼容所有平台）
    if (read_len < 0) {
        perror("Failed to read pcie_dma data");
        free(pcie_dma_buf);
        close(fd);
        return EXIT_FAILURE;
    }
    printf("Read %zd bytes of pcie_dma data (total size: %d)\n", 
           read_len, PCIE_DMA_SIZE);

    // 6. 保存图片到文件（示例：raw格式，目标平台路径可能需调整）
    FILE *fp = fopen("/root/pcie_dma.raw", "wb");  // 若目标平台无"/tmp"，可改为当前目录
    if (fp) {
        size_t write_len = fwrite(pcie_dma_buf, 1, read_len, fp);
        if (write_len != read_len) {
            perror("Failed to write pcie_dma to file");
        } else {
            printf("Image saved to pcie_dma.raw (size: %zu bytes)\n", write_len);
        }
        fclose(fp);
    } else {
        perror("Failed to open pcie_dma file");
    }

    // 释放资源
    free(pcie_dma_buf);
    close(fd);
    return EXIT_SUCCESS;  // 正常退出
}
